iT邦幫忙

2021 iThome 鐵人賽

DAY 2
1
Software Development

猴子都寫得出來的 RISC-V CPU Emulator系列 第 25

RISC-V: R-type 位移指令

  • 分享至 

  • xImage
  •  

今天一樣是簡單的 SLL、SRL、SRA 指令實作,
再一樣為了 Code Stream Logger 做準備,
另外再加碼一個 run 過這份程式的人一定會發現的 Bug 吧。

R-type

指令格式如下:

|31     25|24   20|19   15|14    12|11   7|6       0|
+---------------------------------------------------+
|  func7  |  rs2  |  rs1  | funct3 |  rd  | opcode  |
+---------------------------------------------------+

SLL

rd = rs1 << rs2

|31         25|24   20|19   15|14    12|11   7|6       0|
+-------------------------------------------------------+
|   0000000   |  rs2  |  rs1  |  001   |  rd  | 0110011 |
+-------------------------------------------------------+

SRL

rd = (uint_32)rs1 >> rs2

|31         25|24   20|19   15|14    12|11   7|6       0|
+-------------------------------------------------------+
|   0000000   |  rs2  |  rs1  |  101   |  rd  | 0110011 |
+-------------------------------------------------------+

SRA

rd = rs1 >> rs2

|31         25|24   20|19   15|14    12|11   7|6       0|
+-------------------------------------------------------+
|   0100000   |  rs2  |  rs1  |  101   |  rd  | 0110011 |
+-------------------------------------------------------+

實際程式

github 頁面 Tag: ITDay25

這次的 Bug 是幾天前做 Exception 留下來的,
當時把 Cpu 的 this 放進 shared_ptr ,
又因為 Cpu 在 main 結束的時候會解構,
會有解構兩次造成 crash 的情況,
但是當時想到兩個解法,其中一個是用 Raw Pointer ,
另外一個是在 Cpu 內放一個 shared_ptr,
都不是很漂亮,這個 Bug 又不影響運作過程就沒立刻動手。

雖然另外指定一個什麼事都不做的 Deleter 也沒比 Raw Pointer 好到哪去,
但是不用改使用介面的情況下,是最好的選擇了,
至少這樣做在寫 Executer 的 Unit Test 可以直接用 shared_ptr。

//cpu.cpp
...
	executor->set_cpu(std::shared_ptr<CPU_INTERFACE>(this, [](CPU *p){}));
...

這次正式的把 Decoder 從 Executor 移到 Instruction Decoder 了,
把程式複製貼上再取代真的有夠無聊,
之後找找看有沒有 vim 的功能可以加速這個過程。

改到心很累, Function Name 的部分就之後再改吧,
到時候 Refactor 預計 99% 都會刪掉改寫。

//executor.cpp
...
void EXECUTOR::command_dispatch()
{
	switch (instruction_decoder->get_instruction()) {
		case INSTRUCTION_DECODER_INTERFACE::ADDI_INSTRUCTION_ENUM:
			ADDI_E();
			break;
		case INSTRUCTION_DECODER_INTERFACE::ANDI_INSTRUCTION_ENUM:
			ANDI_E();
			break;
...
//instructionDecoder.cpp
...
INSTRUCTION_DECODER_INTERFACE::Instruction INSTRUCTION_DECODER::get_instruction()
{
	return cmmand_dispatch();
}

INSTRUCTION_DECODER_INTERFACE::Instruction INSTRUCTION_DECODER::cmmand_dispatch()
{
	switch (get_opcode()) {
		case INSTRUCTION_DECODER_INTERFACE::IMM_OP:
			return imm_dispatch();

		case INSTRUCTION_DECODER_INTERFACE::LUI_OP:
			return LUI_INSTRUCTION_ENUM;
...

指令實作的地方把 instruction_name_map 裡的註解取消了,
其他部分大家都很熟就不多說,照著貼:

//instructionDecoderInterface.h
...
		SLL_FN3 = 0b001,
		SRL_FN3 = 0b101,
		SRA_FN3 = 0b101,
...
		SLL_FN7 = 0b0000000,
		SRL_FN7 = 0b0000000,
		SRA_FN7 = 0b0100000,
...
//executor.cpp
...
		case INSTRUCTION_DECODER_INTERFACE::SLL_FN3:
			SLL_E();
			//do not check FN7 for readibility, refactor in future
			break;
		case INSTRUCTION_DECODER_INTERFACE::SRL_FN3:
			switch (instruction_decoder->get_func7()) {
				case INSTRUCTION_DECODER_INTERFACE::SRL_FN7:
					SRL_E();
					break;
				case INSTRUCTION_DECODER_INTERFACE::SRA_FN7:
					SRA_E();
					break;
				default:
					std::cout << "INVALID: Func7 in REG_OP :" << instruction_decoder->get_func3() << std::endl;
					break;
			}
			break;
...
void EXECUTOR::SLL_E()
{
	auto rd = instruction_decoder->get_rd();
	auto rs1 = instruction_decoder->get_rs1();
	auto rs2 = instruction_decoder->get_rs2();

	auto value = register_file->get_value_integer(rs1) << register_file->get_value_integer(rs2);
	register_file->set_value_integer(rd, value);
}

void EXECUTOR::SRL_E()
{
	auto rd = instruction_decoder->get_rd();
	auto rs1 = instruction_decoder->get_rs1();
	auto rs2 = instruction_decoder->get_rs2();

	auto value = (uint32_t)register_file->get_value_integer(rs1) >> register_file->get_value_integer(rs2);
	register_file->set_value_integer(rd, value);
}

void EXECUTOR::SRA_E()
{
	auto rd = instruction_decoder->get_rd();
	auto rs1 = instruction_decoder->get_rs1();
	auto rs2 = instruction_decoder->get_rs2();

	auto value = register_file->get_value_integer(rs1) >> register_file->get_value_integer(rs2);
	register_file->set_value_integer(rd, value);
}

...

上一篇
RISC-V: R-type 小於指令
下一篇
Logger: Code Stream Logger
系列文
猴子都寫得出來的 RISC-V CPU Emulator31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言